let gameMode = 'editor';
function isPlay(): boolean {
	return gameMode === 'play';
}
function setEditorMode(m: 'play' | 'editor') {
	gameMode = m;
}

namespace editor {
	export class Cmd {
		setCmd(undo: () => void, redo: () => void, final?: (cmd: string) => void): void {
			this.undo = undo;
			this.redo = redo;
			this.final = final;
		}

		undo: () => void;

		redo: () => void;

		final: (cmd: string) => void;
	}

	export class CmdObjvalue extends Cmd {
		constructor(obj: ObjectType, key: string, value: unknown) {
			super();
			const old = obj[key];
			this.setCmd(
				() => {
					obj[key] = old;
				},
				() => {
					obj[key] = value;
				},
			);
		}
	}

	export class Cmd2 extends Cmd {
		constructor(c1: Cmd, c2: Cmd) {
			super();
			this.setCmd(
				() => {
					c1.undo();
					c2.undo();
				},
				() => {
					c1.redo();
					c2.redo();
				},
			);
		}
	}

	export class CmdArray extends Cmd {
		constructor(cmds: Cmd[], final?: (cmd: string) => void) {
			super();
			this.setCmd(
				() => {
					for (const c of cmds) {
						c.undo();
					}
				},
				() => {
					for (const c of cmds) {
						c.redo();
					}
				},
				final,
			);
		}
	}

	export class Editor {
		private static instance: Editor;

		public static ins(): Editor {
			if (!this.instance) {
				this.instance = new Editor();
			}
			return this.instance;
		}

		cmds: Cmd[] = [];

		pos = -1;

		undo(): boolean {
			if (this.pos >= 0 && this.pos < this.cmds.length) {
				const cmd = this.cmds[this.pos];
				cmd.undo();
				this.pos--;
				return true;
			}
			return false;
		}

		redo(): boolean {
			if (this.pos + 1 < this.cmds.length) {
				const cmd = this.cmds[this.pos + 1];
				cmd.redo();
				this.pos++;
				return true;
			}
			return false;
		}

		addUndo(undo: () => void, redo: () => void, final?: (cmd: string) => void): void {
			const c = new Cmd();
			c.setCmd(undo, redo, final);
			this.addUndoCmd(c);
		}

		addUndoCmd(c: Cmd): void {
			// this.cmds[pos]
			if (this.pos + 1 < this.cmds.length) {
				const del = this.cmds.splice(this.pos + 1, this.cmds.length - 1 - this.pos);
				for (const d of del) {
					if (d.final) {
						d.final('redo');
					}
				}
			}
			this.cmds.push(c);
			if (this.cmds.length > 100) {
				const cmd = this.cmds.shift();
				if (cmd.final) {
					cmd.final('undo');
				}
			}
			this.pos = this.cmds.length - 1;
		}

		clear(): void {
			for (const cmd of this.cmds) {
				if (cmd.final) {
					cmd.final('clear');
				}
			}
			this.cmds = [];
			this.pos = -1;
		}
	}
}
